#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "../common/general.h"
#include "../common/minilog.h"

#define VERBOSE		INFO | RESULT | WARNING | ERROR | CRITICAL
#define LOGGING		INFO | RESULT | PROCESS
#define COMMENT		"A version of DE, using best vector as base.\nRandom Generator: Mersenne twister"
#define LOGFILENAME	"DERC_02.derc"
#define ALGORITHM	"DERC_02"

/********************************************************************************
	Differential Evolution Parameters
********************/
#define max_iterations		50000
#define NP					100
#define F					1.5
#define CR					.0005
#define test_function		7
#define precision_digits	15
#define sample_step			1e-10
#define eval_stamp			100000

/*
#define dimension			50
#define lower_bound			-1000.0
#define upper_bound			+1000.0
*/
#include "../benchmark/group02fn.h"

/********************************************************************************
	Differential Evolution Global Variables
********************/

struct solution{
	double* vector;
	double fitness;
};
struct solution* population;
struct solution y;
double precision;
int best=-1;
int eval_counter;
char unique_logfilename[256];
#include "../common/common.h"

/********************************************************************************
	Subordinate Functions
********************/
int solComp(const void* a,const void* b) {
	double pa,pb;
	pa=((struct solution*)a)->fitness;
	pb=((struct solution*)b)->fitness;
	if (pa>pb) return 1;
	else if (pa<pb) return -1;
	return 0;
}
void copy(struct solution from,struct solution *to){
	int i;
	to->fitness=from.fitness;
	for(i=0;i<dimension;i++)
		to->vector[i]=from.vector[i];
}
void eval(struct solution *sol){
//	population[index].fitness=func(population[index].vector);
	eval_counter++;
	sol->fitness=func(sol->vector);
}

int init(){
	int i,j;
	eval_counter=0;
	build_min_vector();
	precision=1.0/pow(10,precision_digits);
	population=(struct solution*) malloc (sizeof(struct solution)*NP);
	y.vector=(double*) malloc(sizeof(double)*dimension);
	for(i=0;i<NP;i++){
		population[i].vector=(double*) malloc(sizeof(double)*dimension);
		for(j=0;j<dimension;j++)
			population[i].vector[j]=random3_range();
		eval(&population[i]);
	}
	return 0;
}

void one(){
	int i,j;
	int r,a,b,c;
	double p;
	double pmin=1e200;
	for(i=0;i<NP;i++){
		r=randint3(dimension);
		a=randint3(NP);
		b=randint3(NP);
		c=randint3(NP);
		if (best!=-1) a=best;
		else while(a==i) a=randint3(NP);
		while(b==i || b==a) b=randint3(NP);
		while(c==i || c==b || c==a) c=randint3(NP);
		for(j=0;j<dimension;j++){
			if(random3()<CR || j==r){
				y.vector[j]=population[a].vector[j]+\
				F*(population[b].vector[j]-population[c].vector[j]);
				if(y.vector[j]<lower_bound || y.vector[j]>upper_bound)
					y.vector[j]=random3_range();
			}
			else{
				y.vector[j]=population[i].vector[j];
			}
		}
		eval(&y);
		
		if (y.fitness<population[i].fitness)
			copy(y,&population[i]);
		if (population[i].fitness<pmin){
			best=i;
			pmin=population[i].fitness;
		}
	}
}
/********************************************************************************
	Display and Debug Functions
********************/
void show_info(FILE* out){
	fprintf(out,"/*\n%s\n*/\n",COMMENT);
	L(INFO)fprintf(out,"Logging Filename                 : %s\n",unique_logfilename);
	fprintf(out,"Algorithm                        : %s\n",ALGORITHM);
	fprintf(out,"Test Function                    : %s\n",func_name);
	fprintf(out,"Number of Variables              : %d\n",dimension);
	fprintf(out,"Variables Lower Bound            : %.15f\n",lower_bound);
	fprintf(out,"Variables Upper Bound            : %.15f\n",upper_bound);
	fprintf(out,"Variables Precision              : %.15f\n",precision);
	fprintf(out,"Maximum Iterations               : %d\n",max_iterations);
	fprintf(out,"Population Size (NP)             : %d\n",NP);
	fprintf(out,"Crossover Rate (CR)              : %.15f\n",CR);
	fprintf(out,"F                                : %.15f\n",F);
	fprintf(out,"-------------------------------------------------------------\n");
}

void shome_memory(){
	int i, j;
	for(i=0;i<NP;i++){
		printf("%d] ",i);
		for(j=0;j<dimension;j++)
			printf("%.2f ",population[i].vector[j]);
		printf("=%.2f\n",population[i].fitness);
	}
}

int main(){
	int n=0;
	int i;
	double dist;
	double last_best_fitness=1e200;
	double time_stamp;
	char ufname[100];
	clock_t start;
/*	srand(time(NULL));*/
	mt_init();
	get_unique_filename(LOGFILENAME,unique_logfilename);
	minilog_start(unique_logfilename,VERBOSE,LOGGING);
	init();
	V(INFO)show_info(stdout);
	L(INFO)show_info(LOGFILE);
	start=clock();
	while(n<max_iterations){
		one();
		dist=euclid_dist(population[best].vector,min_vector);
//		VP(PROCESS,"[%06d] f=%.15f\td=%.15f          \r",n,population[best].fitness,dist);
		VP(PROCESS,"[%07d] f=%.15f\td=%.15f          \r",eval_counter,population[best].fitness,dist);
		if(fabs(population[best].fitness-last_best_fitness)>=sample_step || eval_counter%eval_stamp==0) {
//			LP(PROCESS,"%06d\t%.15f\t%.15f\n",n,population[best].fitness,dist);
			LP(PROCESS,"%07d\t%.15f\t%.15f\n",eval_counter,population[best].fitness,dist);
			last_best_fitness=population[best].fitness;
		}
		n++;
	}
	time_stamp=(double) (clock()-start)/CLOCKS_PER_SEC;
//	LP(PROCESS,"%06d\t%.15f\t%.15f\n",--n,population[best].fitness,dist);
	LP(PROCESS,"%07d\t%.15f\t%.15f\n",eval_counter,population[best].fitness,dist);
	VP(PROCESS,"\n");
	LOG(RESULT,"-----------------\n");

	LOG(RESULT,"Best Fitness           : %.15f\n",population[best].fitness);
	LOG(RESULT,"Closest Distance       : %.15f\n",dist);
	LOG(RESULT,"Time                   : %05.2fs\n",time_stamp);
	LOG(RESULT,"-----------------\n");

	for(i=0;i<dimension;i++){
		LOG(RESULT,"x[%d]= %.15f\n",i,population[best].vector[i]);
	}
	minilog_stop();
	return 0;
}
